﻿using System;
using System.Threading;
using FLib;
using LibraryGuard.Interfaces;
using LibraryGuard.Interfaces.Utilities;

namespace LibraryGuard.Utilities
{
	/// <summary>
	/// Default scheduler.
	/// Runs tasks at <see cref="Scheduler.DueTime"/> and ends any execution after <see cref="Scheduler.WorkingTime"/>.
	/// </summary>
	public sealed class Scheduler
		: IScheduler
	{
		#region Logger
		private static NLog.Logger Logger = NLog.LogManager.GetLogger("Scheduler");
		#endregion

		#region Private Fields
		private readonly IMode Mode = null;

		private Timer CurrentTimer = null;
		private bool IsStarted = false;
		#endregion

		#region IScheduler Members
		/// <inheritdoc />
		public TimeSpan DueTime { get; private set; }

		/// <inheritdoc />
		public TimeSpan WorkingTime { get; private set; }

		/// <inheritdoc />
		public void Start()
		{
			Validate.Debug(() => this.IsStarted, v => v.Equal(false));

			Logger.Info("Starting scheduling");
			Logger.Info("Run scheduled(after {0})", this.DueTime);
			this.CurrentTimer = new Timer(this.RunMode, null, this.DueTime, TimeSpan.Zero);
			this.IsStarted = true;
		}

		/// <inheritdoc />
		public void Stop()
		{
			if (this.IsStarted)
			{
				this.CurrentTimer.Dispose();
				this.CurrentTimer = null;
				Logger.Info("Scheduling stopped");
				this.IsStarted = false;
			}
		}
		#endregion

		#region Constructor
		public Scheduler(IMode mode, TimeSpan dueTime, TimeSpan workingTime)
		{
			Validate.Debug(() => mode, v => v.NotNull());
			Validate.Debug(() => workingTime, v => v.That(t => t.TotalHours < 24 && t.TotalHours > 0));
			this.Mode = mode;
			this.DueTime = dueTime;
			this.WorkingTime = workingTime;
		}
		#endregion

		#region Private Methods
		private void RunMode(object obj)
		{
			Logger.Debug("Resuming mode");
			if (this.Mode.State == ModeState.Stopped)
				this.Mode.Start();
			else if (this.Mode.State == ModeState.Paused)
				this.Mode.Resume();

			if (this.CurrentTimer != null)
				this.CurrentTimer.Dispose();
			Logger.Info("Mode resumed, scheduling stop after " + this.WorkingTime);
			this.CurrentTimer = new Timer(this.PauseMode, null, this.WorkingTime, TimeSpan.Zero);
		}

		private void PauseMode(object obj)
		{
			Logger.Debug("Pausing mode");
			this.Mode.Pause();

			//Schedule next run
			if (this.CurrentTimer != null)
				this.CurrentTimer.Dispose();
			Logger.Info("Mode paused, scheduling resume after " + this.DueTime);
			this.CurrentTimer = new Timer(this.RunMode, null, this.DueTime, TimeSpan.Zero);
		}
		#endregion
	}
}
